home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-04-03 | 29.4 KB | 538 lines | [TEXT/pdos] |
- Apple II
- Technical Notes
- _____________________________________________________________________________
- Developer Technical Support
-
- Apple IIGS
- #80: QuickDraw II Clipping
-
- Written by: Eric Soldan March 1990
-
- This Technical Note explains a lot about QuickDraw II operation, specifically
- clipping.
- _____________________________________________________________________________
-
- Before Beginning
-
- Before beginning this Note, some statements, disclaimers, and definitions:
-
- 1. This is not a substitute for the QuickDraw II introduction in the
- Apple IIGS Toolbox Reference, but rather a supplement.
- 2. A pixelmap is a series of bytes that hold pixel data whose
- rectangular shape is defined by a LocInfo structure.
-
- This Note describes in great detail the way that QuickDraw II does things with
- pixelmaps. It begins with a description of the LocInfo structure, which is
- the most important thing to understand in terms of QuickDraw II pixelmap
- management. Once this is understood, this Note covers how it applies to using
- functions such as PPToPort, PaintPixels, and CopyPixels. And once this is
- understood, it then describes how LocInfo structures are used to control
- drawing into a grafPort. (PPToPort is used in this Note. PaintPixels and
- CopyPixels are very close in function to PaintPixels. The information and
- theory in this Note also apply to these calls.)
-
- Understanding the material in this Note should help you better understand the
- entire toolbox. It is surprising how much can be accomplished with the
- toolbox without completely understanding these concepts; it is also surprising
- how much easier programming with the toolbox gets when these concepts are
- fully understood.
-
- Note: Structures are written with C syntax in this Note. In addition,
- this Note uses the screen address 0xE12000L. The possibility of
- shadowing being active and the screen address being 0x12000L is
- ignored.
-
-
- The Beginning
-
- One must begin with the LocInfo structure, which is as follows:
-
- struct LocInfo {
- Word portSCB; /* SCB in low byte */
- Pointer ptrToPixImage; /* ImageRef */
- Word width; /* Width */
- Rect boundsRect; /* BoundsRect */
- };
-
- For this Note, one can change this structure a little bit by calling the width
- element rowBytes. This convention is good because rowBytes is more
- descriptive than width (it indicates that one is measuring the width in bytes)
- and it allows one to use the word "width" elsewhere in this Note without
- confusion. So for the purposes of the Note, the new LocInfo structure
- definition is as follows:
-
- struct LocInfo {
- Word portSCB; /* SCB in low byte */
- Pointer ptrToPixImage; /* ImageRef */
- Word rowBytes; /* Width in bytes*/
- Rect boundsRect; /* BoundsRect */
- };
-
- The ptrToPixImage field is a pointer to some block of bytes in memory. (This
- block of bytes is referred to as the pixImage from here on.) A pixImage
- doesn't have any inherent shape. QuickDraw II deals with it as a rectangle,
- and the LocInfo record defines the rectangularity of it.
-
- When saving a 32,000 byte screen image, one doesn't save the number of bytes
- of which each row consists. One assumes that each row is 160 bytes by
- convention, and this is a safe assumption, since the IIGS video hardware
- expects 160 bytes. But the point is that in the 32,000 bytes of screen data,
- there is no indicator as to the specific size of a row. One must just know
- that it is 160 bytes per row. This size is fine for screen shots, but it is
- not fine when different pixelmaps can be different widths. If they can be
- different widths, then one also needs some information as to what those widths
- are, hence the portSCB, rowBytes, and boundsRect fields in a LocInfo
- structure.
-
- The boundsRect and portSCB fields tell the shape of the pixelmap in pixels,
- the boundsRect tells how many pixels wide and tall the pixelmap is, and the
- portSCB tells how big those pixels are (320-mode pixels are four bits wide and
- 640-mode pixels are two bits wide). One would think that this would be enough
- information to determine the size of the pixImage, but it isn't. The rowBytes
- can be larger than the boundsRect/portSCB would indicate (see Figure 1). This
- situation is legal; it means that some bytes are being wasted, but it is
- legal.
-
- ___________________________ rowBytes ______________________
- | |
- | boundsRect |
- ___________________________________________ ................
- |0,0 .......................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |...........................................|................
- |___________________________________________|................
- .............................................313,97..........
- .......................................................^.....
- .......................................................|.....
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_________|
- _____________________________________________/
-
- Figure 1-Sample LocInfo Structure
-
- One simply has to know the size of the pixImage, since it cannot be determined
- by the LocInfo information. If the pixImage is the screen, then it is 32,000
- bytes. If it is a fixed or locked handle, then one can do a FindHandle on the
- pointer followed by a GetHandleSize on the found handle.
-
- Figure 1 represents a sample LocInfo structure. The portSCB (although not
- pictured) is also relevant, as it determines the size of the pixels. If the
- pixelmap is a 320-mode pixelmap, one could change it to a 640-mode pixelmap by
- changing the portSCB to 640 mode and doubling the width of the boundsRect. In
- doing this conversion, note that rowBytes is not affected and that the
- pixImage does not change size.
-
- In the example illustrated in Figure 1, the pixImage is bigger than the
- boundsRect, but again, this is okay. However, this is not the case for the
- screen, where the rowBytes is 160 and the height of the boundsRect is 200 (the
- size of the screen is exactly equal to 160 * 200 = 32,000).
-
- There are some rules to determining the rowBytes value. First, rowBytes must
- not be too small. This is obvious. Second, rowBytes must be evenly divisible
- by eight. This is not at all obvious, but it is very important. QuickDraw II
- makes some assumptions for speed, and one of them is that rowBytes is a
- multiple of eight.
-
- So much for describing the LocInfo structure. Now for how to use it via
- PPToPort.
-
- PPToPort accepts (among other things) a pointer to a source LocInfo record and
- a pointer to a source rectangle. PPToPort does not use the source rectangle
- directly; it first intersects it with the boundsRect in the LocInfo record,
- and it uses this intersection rectangle instead. This intersection rectangle
- guarantees that the area involved is completely enclosed by the boundsRect
- (and therefore within the pixImage). If the source rectangle is entirely
- outside the boundsRect, then the intersection of the source rectangle and the
- boundsRect is empty, thus nothing is drawn.
-
- ___________________________ rowBytes ______________________
- | |
- | boundsRect |
- ___________________________________________ ................
- |0,0........................................|................
- |.............intersection rectangle........|.........sourceRect
- |.......____________________________________|_____________________________
- |......|50,25 / / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / / / / /|................ |
- |......|/ / / / / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / / / / /|................ |
- |......|/ / / / / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / 313,77|................ |
- |......|____________________________________|_____________________________|
- |...........................................|................ 523,77
- |...........................................|................
- |___________________________________________|................
- .........................................313,97..............
- .......................................................^.....
- .......................................................|.....
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_________|
- _____________________________________________/
-
- Figure 2-Sample LocInfo Structure With sourceRect
-
- Figure 2 contains a sourceRect which is not completely contained by the
- boundsRect; the sourceRect is so wide that it even goes beyond the edge of the
- pixImage. If the entire contents of this rectangle were drawn, the result
- would be quite a mess, since it extends beyond the boundary of the pixelmap.
- However, PPToPort first intersects the sourceRect and the boundsRect, and then
- uses the resulting intersection rectangle (illustrated with a thicker border
- in the figure). PPToPort uses only the contents of the intersection
- rectangle.
-
- Up until now, the boundsRect upper-left corner has always been 0,0. This is
- an easy way to think of it, but it is not necessary. The important thing to
- remember about these rectangles is their relation to one another. If one were
- to offset both the boundsRect and sourceRect in this example, the values for
- the corners of the rectangles would change, but the relationship between the
- two rectangles would stay the same. Figure 3 illustrates the same example if
- one were to offset both rectangles by -60,-45.
-
- ___________________________ rowBytes ______________________
- | |
- | boundsRect |
- ___________________________________________ ................
- |-60,-45....................................|................
- |.............intersection rectangle........|.........sourceRect
- |.......____________________________________|_____________________________
- |......|-10,-20 / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / / / / /|................ |
- |......|/ / / / / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / / / / /|................ |
- |......|/ / / / / / / / / / / / / / / / / / |................ |
- |......| / / / / / / / / / / / / / / 253,32|................ |
- |......|____________________________________|_____________________________|
- |...........................................|................ 463,32
- |...........................................|................
- |___________________________________________|................
- .........................................253,52..............
- .......................................................^.....
- .......................................................|.....
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_________|
- _____________________________________________/
-
- Figure 3-Sample LocInfo Structure Offset by -60,-45
-
- Notice that the same area of the pixImage is involved, even though the
- boundsRect and sourceRect are offset. When one offsets both the boundsRect
- and sourceRect by the same amount, the referenced part of the pixImage does
- not change--this is an important concept.
-
- Time to ask a question that is answered shortly: "Why isn't the upper-left
- corner of the boundsRect always 0,0?" Because the LocInfo record isn't always
- a source LocInfo record. It can also be a destination LocInfo record, and the
- most common pixelmap to which a destination LocInfo record refers is the
- screen.
-
- If you had not noticed, the discussion changes gears here--to discuss LocInfo
- records that indicate a destination pixelmap. Basically, everything is the
- same as has been described with two exceptions. First, destination pixelmaps
- do not have a sourceRect. Instead there is a rectangle that describes some
- portion of the destination pixelmap, and this rectangle is called the
- portRect. Second, the LocInfo record is part of a grafPort, and each grafPort
- has a LocInfo record as part of the grafPort data structure.
-
- It is important to remember that a LocInfo record can be used as either a
- source or destination LocInfo. All a LocInfo record does is define some bytes
- in memory as a pixImage. Even the screen, which is usually used as a
- destination pixelmap, can be used as a source pixelmap. There could be
- situations where one might want to take part of the screen and copy it into
- some off-screen pixelmap, and in this case, the screen would be a source of
- pixel data, not a destination.
-
- In the case of the screen pixelmap, there are no wasted bytes in the pixImage,
- as all of the screen bytes are enclosed by the boundsRect. The screen width
- of 160 is evenly divisible by eight, so there is no slop at the right edge,
- and there are no extra rows hanging off the bottom of the boundsRect.
-
- Figure 4 shows a sample LocInfo and portRect (every grafPort has a LocInfo and
- a portRect).
-
- __________________ rowBytes _______________
- | |
- | boundsRect |
- ___________________________________________
- |0,0........................................|
- |.............intersection rectangle........| portRect
- |.......____________________________________|_____________________________
- |......|98,54 / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / / / / /| |
- |......|/ / / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / / / / /| |
- |......|/ / / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / 640,143| |
- |......|____________________________________|_____________________________|
- |...........................................| 917,143
- |........................................<__|______
- |___________________________________________| |
- 640,200 |
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_____|
- In the case of the screen, this is $E12000. /
- ____________________________________________/
-
- Figure 4-Sample LocInfo and portRect
-
- Following are two important points to remember:
-
- 1. Every grafPort works in local (not global) coordinates (local
- coordinates are defined soon).
- 2. The origin of the grafPort is the upper-left corner of the
- portRect. There is no GetOrigin call; there is a SetOrigin call,
- but no GetOrigin. To get the origin of a grafPort, one needs to
- do a GetPortRect call, and then look at the upper-left corner to
- determine the current origin of the grafPort. This is the way to
- get the origin.
-
- In the case of Figure 4, local and global coordinate systems are the same, as
- is always the case when the boundsRect has an upper-left corner of 0,0 (which
- it seldom does). So, for this exceptional case, one doesn't need a definition
- of local coordinates. In the global coordinate system, the upper-left corner
- of the screen is 0,0. In local coordinates, the upper-left corner of the
- screen is whatever the boundsRect says it is. So when the upper-left corner
- of the boundsRect is 0,0, the global and local coordinate systems are the
- same.
-
- In Figure 4, if one tried to draw something to point 0,0, it would not draw--it
- would be clipped because it is outside the portRect. So even if one tried to
- draw there, it would not change point 0,0. If a user moved a mouse to that
- location and an application performed a GetMouse (which returns the mouse
- location in the local coordinates of the current grafPort), it would return
- 0,0 as the mouse location.
-
- If one did a SetOrigin(0,0), then the boundsRect and portRect would be offset
- by the difference between the old and new origins. Both rectangles would be
- offset, so the relationship between them would remain the same, as Figure 5
- illustrates.
-
- __________________ rowBytes _______________
- | |
- | boundsRect |
- ___________________________________________
- |-98,-54....................................|
- |.............intersection rectangle........| portRect
- |.......____________________________________|_____________________________
- |......|0,0 / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / / / / /| |
- |......|/ / / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / / / / /| |
- |......|/ / / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / 542,89| |
- |......|____________________________________|_____________________________|
- |...........................................| 819,89
- |........................................<__|______
- |___________________________________________| |
- 542,146 |
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_____|
- In the case of the screen, this is $E12000. /
- ____________________________________________/
-
- Figure 5-Sample LocInfo and portRect, Both Offset
-
- Now if a user moves a mouse to the upper-left corner of the screen, a call to
- GetMouse returns a value of -98,-54, as expected, and if a user moves the
- mouse to the upper-left corner of the portRect, a call to GetMouse returns
- 0,0, again as expected. This is how origins work and how the conceptual
- drawing space relates to the grafPort. The boundsRect of the grafPort (in the
- LocInfo record of the grafPort) and the portRect of the grafPort are offset
- when one calls SetOrigin. It is that simple.
-
- Now that it is simple, time to complicate matters with one more player in the
- QuickDraw II clipping world: the visRgn.
-
- The visRgn exists for one purpose: to cause more clipping. It never causes
- anything to be clipped less than the portRect does, and in the case of a top
- window that is completely visible, the visRgn and the portRect are exactly the
- same size. Even more than that, the enclosing rectangle for the visRgn (every
- region has an enclosing rectangle) is this case would be exactly the same as
- that of the portRect. This all makes sense when one looks at the purpose of a
- visRgn. Again, the visRgn can only cause more clipping. If the entire window
- is visible, one does not want more clipping, so a visRgn the same size as the
- portRect guarantees that it does not clip any more than the portRect, as it
- must clip the same amount.
-
- The visRgn is a different size than the portRect when the window is not the
- top window and part of it is overlapped (or if part of the window is off the
- screen). The part that is overlapped is excluded from the visRgn, and this
- excluded part is clipped to protect the window above from being drawn upon.
- This is how window clipping works. This is all there is to it.
-
- Figure 6 enhances Figure 5 by adding an overlapping window to demonstrate the
- visRgn.
-
- __________________ rowBytes _______________
- | |
- | boundsRect of current grafPort |
- ___________________________________________
- |-98,-54....................................|
- |.............intersection rectangle........| portRect of current grafPort
- |.......____________________________________|_____________________________
- |......|0,0 / / / / / / / / / / / / / / / / | |
- |......| / / / / / / / / / / / / / / / / / /| |
- |......|/ / / / / / / / / / / ______________|___________________ |
- |......| / / / / / / / / / / | | | |
- |......|/ / / / / / / / / / /| | | |
- |......| / ^ / / / / / / / | 542,89| | |
- |......|____|________________|______________|___________________|_________|
- |...........|................| | | 819,89
- |..^........|................| | |
- |__|________|________________|______________| |
- | | | 542,146 |
- | | |__________________________________|
- | | portRect of some overlapping window
- | | ____________________________
- | |___/ visRgn of current grafPort
- | \____________________________
- | _____________________________________________
- |____________/ Block of bytes pointed to by ptrToPixImage.
- \ In the case of the screen, this is $E12000.
- \____________________________________________
-
- Figure 6-Sample LocInfo and portRect With Overlapping Window
-
- What happens to the visRgn during a SetOrigin? Remember that the boundsRect
- and portRect get offset. The visRgn does too. Again, if all of these
- elements are offset together, then the relationship between them remains the
- same; they stay the same, relative to one another. (For more information, see
- Einstein's theory of general relativity.)
-
- The final component for clipping is the clipRgn, which is the application's
- property and, therefore, the application's responsibility. The system sets
- the clipRgn about as big as it can get to start (much bigger than the
- portRect); this is often referred to as arbitrarily large, even though it
- isn't so arbitrary. The system creates all grafPort structures with a large
- clipRgn, and this can be a problem for certain types of QuickDraw II
- operations. Since the clipRgn already reaches to the borders of the
- conceptual drawing space, it cannot be offset; it is effectively stuck, due to
- its size. It is a good practice to make the clipRgn smaller than the system
- default.
-
- SetOrigin does not offset the clipRgn. (This is why the size problem with a
- big clipRgn is not so apparent.) The clipRgn is the only clipping component
- that is not offset by SetOrigin, and one should consider this when using
- clipRgn for clipping effects, since an application must remember to offset it
- if it needs to be offset.
-
- Now with all of the fundamentals out of the way, it is time to play some
- grafPort clipping games. As a refresher, there are four clipping components
- in a grafPort: the boundsRect, the portRect, the visRgn, and the clipRgn.
-
- If an application creates its own off-screen grafPort structures, then it can
- do as it wishes with all four clipping components. After all, if it has the
- responsibility to set them up in the first place, it should have the right to
- change them. If, however, the Window Manager creates the grafPort structures,
- then an application should keeps its figurative hands off certain clipping
- components, namely the boundsRect and the visRgn. The clipRgn, by definition,
- is the application's to do with as it sees fit, and if careful, an application
- can also change the portRect. Changing the portRect can be very useful, but
- one needs to be careful and fully understand all of the ramifications.
-
- So, why would one change the portRect, and how would one do it?
-
- Another figure is in order.
-
- __________________ rowBytes _______________
- | |
- | boundsRect |
- ___________________________________________
- |-98,-54....................................|
- |.............intersection rectangle........| portRect
- |.......____________________________________|_____________________________
- |......|0,0 / /|100,0 / / / / / / / / / / / | |
- |......| / / / | / / / / / / / / / / / / / /| |
- |......|/ / / /|/ / / / / / / / / / / / / / | |
- |......| / / / | / / / / / / / / / / / / / /| |
- |......|/ / / /|/ / / / / / / / / / / / / / | |
- |......| / / / | / / / / / / / / / / 542,89| |
- |......|_______|____________________________|_____________________________|
- |...........................................| 819,89
- |........................................<__|______
- |___________________________________________| |
- 542,146 |
- |
- _____________________________________________ |
- Block of bytes pointed to by ptrToPixImage. \_____|
- In the case of the screen, this is $E12000. /
- ____________________________________________/
-
- Figure 7-Sample LocInfo and Modified portRect
-
- One can use the GetPortRect call to get the portRect for the current grafPort.
- One can then modify it, and then use the SetPortRect call to inform the
- grafPort about the change. Why do this? In Figure 7, the dotted line
- represents the new left edge of the portRect after the modification (a simple
- modification of adding 100 to the old value of zero).
-
- Note that changing the portRect in this way changes the relationship between
- the portRect and the boundsRect. Anything drawn from 0 to 99 (x coordinate)
- is clipped, since it is outside the new (modified) portRect. Before the
- modification, anything drawn from 0 to 99 would have affected the screen.
-
- This modification may cause the portRect to be smaller than the visRgn. This
- is okay, since the visRgn can only cause more clipping, not less. So, all of
- this works just fine. Note that the origin changed when the left edge of the
- portRect changed. The upper-left corner of the portRect is always the origin,
- and an application changed it. The origin changed without a SetOrigin call.
- (Scary, huh?)
-
- One could have done exactly the same thing by making a clipRgn to exclude the
- x coordinates from 0 to 99. However, here is something cool. After the
- modification, do a SetOrigin(0,0), which sets the upper-left corner of the
- shrunk portRect to 0,0. One cannot accomplish this sort of thing as simply by
- making a clipRgn. One can effectively move where an origin of 0,0 is the
- screen, and just building a clipRgn to exclude some part of the screen does
- not accomplish this.
-
- Why would one want to change where 0,0 is on the screen? This sort of trick
- is very useful for adding rulers to a document window, for example. One of
- the problems with rulers is that they should not scroll with the rest of a
- document. Unfortunately, TaskMaster, if allowed to handle scrolling, doesn't
- know about a ruler at the top of a window and scrolls it with the rest of the
- window's content area. By changing the portRect so that the ruler is not
- inside of it, one can keep TaskMaster from scrolling it. In a draw procedure,
- when it is necessary to draw the ruler, grow the portRect, set the origin to
- 0,0, and then draw the ruler. Once it is drawn, set the portRect back to the
- smaller size to protect the ruler again.
-
- Another reason one might want to do this is if an application uses a split
- window (where the top of the window may show a different part of the document
- than the bottom). Changing the portRect has the advantage that the upper-left
- corner of the portRect is always the origin, so it makes mapping document
- coordinates easier.
-
- Another advantage to using the portRect in this way is that it keeps the
- clipRgn free for other purposes. Being able to separate types of clipping to
- either the portRect or the clipRgn keeps the clipRgn from being overused.
-
- As a final note, it should be observed that the only clipping that is done is
- on a destination pixelmap. There is no clipping on a source pixelmap. There
- is no need. All the clipping needed is done at the destination end, so it
- would be wasteful to clip twice.
-
- This finishes the discussion about QuickDraw II and how the boundsRect,
- portRect, visRgn, and clipRgn work together to accomplish clipping. Hopefully
- this Note answers more questions than it creates.
-
-
- Further Reference
- _____________________________________________________________________________
- o Apple IIGS Toolbox Reference, Volume 2
- o Relativity the Special and General Theory (1920)
-
-